Amazon Cognito ユーザープールをCloudFormationで作成し、SES設定等のカスタマイズもしてみた
はじめに
以前、管理者主導でユーザー登録を行う運用ケースにおける、Amazon Cognito ユーザープールの設定すべき値や作成方法を解説しました。
構成は以下の通りです。
今回は、そのユーザープールをAWS CloudFormationで作成します。
さらに、ユーザープール作成後、以下の順序でCloudFormationを使用してALBとの統合やCognitoドメインをカスタムドメインへの変更なども行います。
- Cognito ユーザープールを作成
- ユーザー作成
- ALBと統合
- Cognitoドメインをカスタムドメイン
- メールドメインをSESに変更
CloudFormation を使用することで、Cognito ユーザープールの作成から各種設定まで、インフラストラクチャをコードとして管理できます。
これにより、環境の再現性が高まり、開発からテスト、本番環境への展開がスムーズになります。
前提条件
- 本記事では、Cognitoユーザープールの作成に焦点を当てています。その他の環境構築については、以下の記事を参考に、下記の構成で事前に完了していることを前提としています。
- 以下の記事を参考に、SES IDを事前に作成済みであること
https://dev.classmethod.jp/articles/registered-free-domain-on-route53-and-authenticated-ses/ - Amazon Route 53 パブリックホストゾーン(または他のDNSプロバイダー)で以下の設定が完了していること(
example.com
は仮のドメイン名です。各自が取得したドメイン名に置き換えてください)- ホスト名
example.com
を作成済み - レコード名
example.com
に対して、AレコードでALBのDNS名を値として設定済み - レコード名
auth.example.com
を、Hosted UIのログイン画面用カスタムドメインとして使用予定
- ホスト名
- 東京リージョンとバージニアリージョンで、ドメイン名
example.com
(*.example.comを含む)のACM証明書が発行済みであること
Cognitoユーザープール作成
以下のCloudFormationテンプレートを使用してスタックを作成します。
AWSTemplateFormatVersion: 2010-09-09
Description: Cognito User Pool
Parameters:
DomainName:
Type: String
Description: Domain name for the callback URL (e.g., example.com)
Default: example.com
UserPoolName:
Type: String
Description: User Pool name
Default: admin-managed-user-pool
Resources:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Ref UserPoolName
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: true
RequireUppercase: true
TemporaryPasswordValidityDays: 7
AdminCreateUserConfig:
AllowAdminCreateUserOnly: true
AccountRecoverySetting:
RecoveryMechanisms:
- Name: verified_email
Priority: 1
UsernameConfiguration:
CaseSensitive: false
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_CODE
MfaConfiguration: 'ON'
EnabledMfas:
- SOFTWARE_TOKEN_MFA
Schema:
- Name: email
AttributeDataType: String
Mutable: true
Required: true
EmailConfiguration:
EmailSendingAccount: COGNITO_DEFAULT
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref CognitoUserPool
ClientName: !Ref UserPoolName
GenerateSecret: true
ExplicitAuthFlows:
- ALLOW_USER_SRP_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
PreventUserExistenceErrors: ENABLED
AllowedOAuthFlows:
- code
AllowedOAuthScopes:
- email
- openid
- phone
AllowedOAuthFlowsUserPoolClient: true
SupportedIdentityProviders:
- COGNITO
CallbackURLs:
- !Sub https://${DomainName}/oauth2/idpresponse
LogoutURLs: []
CognitoUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
Domain: !Ref UserPoolName
UserPoolId: !Ref CognitoUserPool
Outputs:
UserPoolDomain:
Description: Domain of the Cognito User Pool
Value: !Sub ${CognitoUserPoolDomain}.auth.${AWS::Region}.amazoncognito.com
CallbackURL:
Description: Callback URL for the Cognito User Pool Client
Value: !Sub https://${DomainName}/oauth2/idpresponse
ドメイン名とユーザープール名をパラメータとして記載し、スタックを作成します。
スタックを作成すると、ユーザープールが作成されます。
ユーザーを追加
次に、ユーザープールにサンプルユーザーを追加します。以下のテンプレートを使用してスタックを更新します。
AWSTemplateFormatVersion: 2010-09-09
Description: Cognito User Pool
Parameters:
DomainName:
Type: String
Description: Domain name for the callback URL (e.g., example.com)
Default: example.com
UserPoolName:
Type: String
Description: User Pool name
Default: admin-managed-user-pool
+ CognitoUserEmail:
+ Type: String
+ CognitoUserName:
+ Type: String
Resources:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Ref UserPoolName
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: true
RequireUppercase: true
TemporaryPasswordValidityDays: 7
AdminCreateUserConfig:
AllowAdminCreateUserOnly: true
AccountRecoverySetting:
RecoveryMechanisms:
- Name: verified_email
Priority: 1
UsernameConfiguration:
CaseSensitive: false
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_CODE
MfaConfiguration: 'ON'
EnabledMfas:
- SOFTWARE_TOKEN_MFA
Schema:
- Name: email
AttributeDataType: String
Mutable: true
Required: true
EmailConfiguration:
EmailSendingAccount: COGNITO_DEFAULT
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref CognitoUserPool
ClientName: !Ref UserPoolName
GenerateSecret: true
ExplicitAuthFlows:
- ALLOW_USER_SRP_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
PreventUserExistenceErrors: ENABLED
AllowedOAuthFlows:
- code
AllowedOAuthScopes:
- email
- openid
- phone
AllowedOAuthFlowsUserPoolClient: true
SupportedIdentityProviders:
- COGNITO
CallbackURLs:
- !Sub https://${DomainName}/oauth2/idpresponse
LogoutURLs: []
CognitoUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
Domain: !Ref UserPoolName
UserPoolId: !Ref CognitoUserPool
+ UserPoolUser:
+ Type: AWS::Cognito::UserPoolUser
+ Properties:
+ UserPoolId: !Ref CognitoUserPool
+ Username: !Ref CognitoUserName
+ DesiredDeliveryMediums:
+ - EMAIL
+ UserAttributes:
+ - Name: email
+ Value: !Ref CognitoUserEmail
+ - Name: email_verified
+ Value: 'true'
Outputs:
UserPoolDomain:
Description: Domain of the Cognito User Pool
Value: !Sub ${CognitoUserPoolDomain}.auth.${AWS::Region}.amazoncognito.com
CallbackURL:
Description: Callback URL for the Cognito User Pool Client
Value: !Sub https://${DomainName}/oauth2/idpresponse
ユーザー名とユーザーのメールアドレスをパラメータとして指定し、スタックを更新します。
スタック作成後、メールアドレスに、ユーザー名とパスワードが送信されます。
マネジメントコンソールからもユーザーが確認できます。
この設定により、管理者主導のユーザー登録プロセスを実現し、セキュアなユーザー管理が可能になります。
管理者がユーザーを作成し、初期パスワードを設定することで、不正なアカウント作成を防ぎ、より厳格なユーザー管理が可能となります。
ALBと統合
続いて、アプリケーションにログインできるようにユーザープールをALBと統合します。以下のテンプレートを使用してスタックを更新します。
AWSTemplateFormatVersion: 2010-09-09
Description: Cognito User Pool
Parameters:
DomainName:
Type: String
Description: Domain name for the callback URL (e.g., example.com)
Default: example.com
UserPoolName:
Type: String
Description: User Pool name
Default: admin-managed-user-pool
CognitoUserEmail:
Type: String
CognitoUserName:
Type: String
+ ExistingALBListenerArn:
+ Type: String
+ Description: ARN of the existing ALB Listener (usually the HTTPS:443 listener)
+ Default: arn:aws:elasticloadbalancing:ap-northeast-1:012345678901:listener/app/alb-name/xxxx/xxxx
+ ExistingTargetGroupArn:
+ Type: String
+ Description: ARN of the existing Target Group
+ Default: arn:aws:elasticloadbalancing:ap-northeast-1:012345678901:targetgroup/tg-name/xxxx
Resources:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Ref UserPoolName
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: true
RequireUppercase: true
TemporaryPasswordValidityDays: 7
AdminCreateUserConfig:
AllowAdminCreateUserOnly: true
AccountRecoverySetting:
RecoveryMechanisms:
- Name: verified_email
Priority: 1
UsernameConfiguration:
CaseSensitive: false
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_CODE
MfaConfiguration: 'ON'
EnabledMfas:
- SOFTWARE_TOKEN_MFA
Schema:
- Name: email
AttributeDataType: String
Mutable: true
Required: true
EmailConfiguration:
EmailSendingAccount: COGNITO_DEFAULT
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref CognitoUserPool
ClientName: !Ref UserPoolName
GenerateSecret: true
ExplicitAuthFlows:
- ALLOW_USER_SRP_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
PreventUserExistenceErrors: ENABLED
AllowedOAuthFlows:
- code
AllowedOAuthScopes:
- email
- openid
- phone
AllowedOAuthFlowsUserPoolClient: true
SupportedIdentityProviders:
- COGNITO
CallbackURLs:
- !Sub https://${DomainName}/oauth2/idpresponse
LogoutURLs: []
CognitoUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
Domain: !Ref UserPoolName
UserPoolId: !Ref CognitoUserPool
UserPoolUser:
Type: AWS::Cognito::UserPoolUser
Properties:
UserPoolId: !Ref CognitoUserPool
Username: !Ref CognitoUserName
DesiredDeliveryMediums:
- EMAIL
UserAttributes:
- Name: email
Value: !Ref CognitoUserEmail
- Name: email_verified
Value: 'true'
+ ALBListenerRule:
+ Type: AWS::ElasticLoadBalancingV2::ListenerRule
+ Properties:
+ Actions:
+ - Type: authenticate-cognito
+ Order: 1
+ AuthenticateCognitoConfig:
+ UserPoolArn: !GetAtt CognitoUserPool.Arn
+ UserPoolClientId: !Ref CognitoUserPoolClient
+ UserPoolDomain: !Ref CognitoUserPoolDomain
+ - Type: forward
+ Order: 2
+ TargetGroupArn: !Ref ExistingTargetGroupArn
+ Conditions:
+ - Field: host-header
+ Values:
+ - !Ref DomainName
+ ListenerArn: !Ref ExistingALBListenerArn
+ Priority: 1
Outputs:
UserPoolDomain:
Description: Domain of the Cognito User Pool
Value: !Sub ${CognitoUserPoolDomain}.auth.${AWS::Region}.amazoncognito.com
CallbackURL:
Description: Callback URL for the Cognito User Pool Client
Value: !Sub https://${DomainName}/oauth2/idpresponse
作成済みのALBのリスナールールとターゲットグループのARNをパラメータとして指定し、スタックを更新します。
スタック更新後、リスナールールに反映されたことが確認できます。
ブラウザでhttps://example.com/
にアクセスしてみると、Hosted UI画面に遷移しました。
上記の赤枠の通り、Cognitoドメインが利用されているので、次章でカスタムドメインに変更します。
ALB との統合により、アプリケーションへのアクセス制御を簡単に実装できます。ユーザーは Cognito による認証を経てからアプリケーションにアクセスすることになり、セキュリティが向上します。
また、ALB が認証を処理するため、アプリケーション側の実装を簡素化できます。
カスタムドメイン
次に、Cognitoドメインをカスタムドメインに変更します。以下のテンプレートを使用してスタックを更新します。
AWSTemplateFormatVersion: 2010-09-09
Description: Cognito User Pool
Parameters:
DomainName:
Type: String
Description: Domain name for the callback URL (e.g., example.com)
Default: example.com
UserPoolName:
Type: String
Description: User Pool name
Default: admin-managed-user-pool
CognitoUserEmail:
Type: String
CognitoUserName:
Type: String
ExistingALBListenerArn:
Type: String
Description: ARN of the existing ALB Listener (usually the HTTPS:443 listener)
Default: arn:aws:elasticloadbalancing:ap-northeast-1:012345678901:listener/app/alb-name/xxxx/xxxx
ExistingTargetGroupArn:
Type: String
Description: ARN of the existing Target Group
Default: arn:aws:elasticloadbalancing:ap-northeast-1:012345678901:targetgroup/tg-name/xxxx
+ CertificateArn:
+ Type: String
+ Description: >-
+ ARN of the ACM certificate for the custom domain.
+ This certificate MUST be in the us-east-1 (N. Virginia) region,
+ regardless of the region where this stack is deployed.
+ Default: arn:aws:acm:us-east-1:012345678901:certificate/xxx
Resources:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Ref UserPoolName
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: true
RequireUppercase: true
TemporaryPasswordValidityDays: 7
AdminCreateUserConfig:
AllowAdminCreateUserOnly: true
AccountRecoverySetting:
RecoveryMechanisms:
- Name: verified_email
Priority: 1
UsernameConfiguration:
CaseSensitive: false
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_CODE
MfaConfiguration: 'ON'
EnabledMfas:
- SOFTWARE_TOKEN_MFA
Schema:
- Name: email
AttributeDataType: String
Mutable: true
Required: true
EmailConfiguration:
EmailSendingAccount: COGNITO_DEFAULT
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref CognitoUserPool
ClientName: !Ref UserPoolName
GenerateSecret: true
ExplicitAuthFlows:
- ALLOW_USER_SRP_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
PreventUserExistenceErrors: ENABLED
AllowedOAuthFlows:
- code
AllowedOAuthScopes:
- email
- openid
- phone
AllowedOAuthFlowsUserPoolClient: true
SupportedIdentityProviders:
- COGNITO
CallbackURLs:
- !Sub https://${DomainName}/oauth2/idpresponse
LogoutURLs: []
CognitoUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
- Domain: !Ref UserPoolName
+ Domain: !Sub auth.${DomainName}
UserPoolId: !Ref CognitoUserPool
+ CustomDomainConfig:
+ CertificateArn: !Ref CertificateArn
UserPoolUser:
Type: AWS::Cognito::UserPoolUser
Properties:
UserPoolId: !Ref CognitoUserPool
Username: !Ref CognitoUserName
DesiredDeliveryMediums:
- EMAIL
UserAttributes:
- Name: email
Value: !Ref CognitoUserEmail
- Name: email_verified
Value: 'true'
ALBListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- Type: authenticate-cognito
Order: 1
AuthenticateCognitoConfig:
UserPoolArn: !GetAtt CognitoUserPool.Arn
UserPoolClientId: !Ref CognitoUserPoolClient
UserPoolDomain: !Ref CognitoUserPoolDomain
- Type: forward
Order: 2
TargetGroupArn: !Ref ExistingTargetGroupArn
Conditions:
- Field: host-header
Values:
- !Ref DomainName
ListenerArn: !Ref ExistingALBListenerArn
Priority: 1
Outputs:
UserPoolDomain:
Description: Domain of the Cognito User Pool
- Value: !Sub ${CognitoUserPoolDomain}.auth.${AWS::Region}.amazoncognito.com
+ Value: !Ref CognitoUserPoolDomain
CallbackURL:
Description: Callback URL for the Cognito User Pool Client
Value: !Sub https://${DomainName}/oauth2/idpresponse
テンプレートのパラメータに、前提条件で述べたバージニアリージョンのACMのARNを指定してスタックを更新します。
カスタムドメインを作成すると、自動でCloudFrontのDNS名が発行されます。CloudFrontのエイリアスターゲットをコピーしておきます。
Route 53で新しいレコードを作成します。レコード名をauth.example.com
、レコードタイプをCNAMEとし、値には先ほどコピーしたCloudFrontのDNS名を入力します。
正しく設定されていれば、カスタムドメインであるhttps://auth.example.com/login?client_id=xxxx&redirect_uri=xxxx
のログイン画面に自動的にリダイレクトされます。
カスタムドメインの設定により、ブランディングを維持しつつ、ユーザーエクスペリエンスを向上させることができます。
ユーザーは組織の公式ドメインでログイン画面にアクセスすることになり、信頼性と一貫性のあるユーザー体験を提供できます。
CloudFormationではなく、マネジメントコンソールから対応する場合は以下の記事を参考ください。
メールドメインをSESに変更
最後に、メールドメインをCognitoドメインから作成済みのSESに変更します。以下のテンプレートを使用してスタックを更新します。
AWSTemplateFormatVersion: 2010-09-09
Description: Cognito User Pool
Parameters:
DomainName:
Type: String
Description: Domain name for the callback URL (e.g., example.com)
Default: example.com
UserPoolName:
Type: String
Description: User Pool name
Default: admin-managed-user-pool
CognitoUserEmail:
Type: String
CognitoUserName:
Type: String
ExistingALBListenerArn:
Type: String
Description: ARN of the existing ALB Listener (usually the HTTPS:443 listener)
Default: arn:aws:elasticloadbalancing:ap-northeast-1:012345678901:listener/app/alb-name/xxxx/xxxx
ExistingTargetGroupArn:
Type: String
Description: ARN of the existing Target Group
Default: arn:aws:elasticloadbalancing:ap-northeast-1:012345678901:targetgroup/tg-name/xxxx
CertificateArn:
Type: String
Description: >-
ARN of the ACM certificate for the custom domain.
This certificate MUST be in the us-east-1 (N. Virginia) region,
regardless of the region where this stack is deployed.
Default: arn:aws:acm:us-east-1:012345678901:certificate/xxx
+ EmailDomain:
+ Type: String
+ Description: Email address to use as the source for Cognito emails
+ Default: example.com
+ EmailSenderName:
+ Type: String
+ Description: Name to use as the sender for Cognito emails
+ Default: classmethod
Resources:
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Ref UserPoolName
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: true
RequireNumbers: true
RequireSymbols: true
RequireUppercase: true
TemporaryPasswordValidityDays: 7
AdminCreateUserConfig:
AllowAdminCreateUserOnly: true
AccountRecoverySetting:
RecoveryMechanisms:
- Name: verified_email
Priority: 1
UsernameConfiguration:
CaseSensitive: false
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_CODE
MfaConfiguration: 'ON'
EnabledMfas:
- SOFTWARE_TOKEN_MFA
Schema:
- Name: email
AttributeDataType: String
Mutable: true
Required: true
- EmailConfiguration:
- EmailSendingAccount: COGNITO_DEFAULT
+ EmailConfiguration:
+ EmailSendingAccount: DEVELOPER
+ From: !Sub ${EmailSenderName} <no-reply@${EmailDomain}>
+ SourceArn: !Sub arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/${EmailDomain}
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref CognitoUserPool
ClientName: !Ref UserPoolName
GenerateSecret: true
ExplicitAuthFlows:
- ALLOW_USER_SRP_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
PreventUserExistenceErrors: ENABLED
AllowedOAuthFlows:
- code
AllowedOAuthScopes:
- email
- openid
- phone
AllowedOAuthFlowsUserPoolClient: true
SupportedIdentityProviders:
- COGNITO
CallbackURLs:
- !Sub https://${DomainName}/oauth2/idpresponse
LogoutURLs: []
CognitoUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
Domain: !Sub auth.${DomainName}
UserPoolId: !Ref CognitoUserPool
CustomDomainConfig:
CertificateArn: !Ref CertificateArn
UserPoolUser:
Type: AWS::Cognito::UserPoolUser
Properties:
UserPoolId: !Ref CognitoUserPool
Username: !Ref CognitoUserName
DesiredDeliveryMediums:
- EMAIL
UserAttributes:
- Name: email
Value: !Ref CognitoUserEmail
- Name: email_verified
Value: 'true'
ALBListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- Type: authenticate-cognito
Order: 1
AuthenticateCognitoConfig:
UserPoolArn: !GetAtt CognitoUserPool.Arn
UserPoolClientId: !Ref CognitoUserPoolClient
UserPoolDomain: !Ref CognitoUserPoolDomain
- Type: forward
Order: 2
TargetGroupArn: !Ref ExistingTargetGroupArn
Conditions:
- Field: host-header
Values:
- !Ref DomainName
ListenerArn: !Ref ExistingALBListenerArn
Priority: 1
Outputs:
UserPoolDomain:
Description: Domain of the Cognito User Pool
Value: !Ref CognitoUserPoolDomain
CallbackURL:
Description: Callback URL for the Cognito User Pool Client
Value: !Sub https://${DomainName}/oauth2/idpresponse
パラメータにメールドメイン名と送信者の名前を指定し、スタックを更新します。
設定後、テストメールを送信すると、指定したSESのメールドメインから正常に送信されることが確認できました。
SES との統合により、組織のドメインからカスタマイズされたメールを送信できるようになり、ユーザーの信頼を得やすくなるとともに、Cognitoのデフォルトのメール送信数制限を超えて、より大量のメール配信が可能になります。
CloudFormationではなく、マネジメントコンソールから対応する場合は以下の記事を参考ください。
最後に
本記事では、Amazon Cognito ユーザープールを CloudFormation を使用して構築し、さらに ALB との統合、カスタムドメインの設定、SES を用いたメール送信の設定まで、一連の流れを解説しました。
以下が今回のポイントです。
-
CloudFormation を使用することで、Cognito ユーザープールの作成から各種設定まで、インフラストラクチャをコードとして管理できます。
-
管理者主導のユーザー登録プロセスを実現し、セキュアなユーザー管理を可能にしました。
-
ALB との統合により、アプリケーションへのアクセス制御を簡単に実装できます。
-
カスタムドメインの設定により、ブランディングを維持しつつ、ユーザーエクスペリエンスを向上させることができます。
-
SES との統合により、組織のドメインからカスタマイズされたメールを送信できるようになり、ユーザーの信頼を得やすくなるとともに、Cognitoのデフォルトのメール送信数制限を超えて、より大量のメール配信が可能になります。
CloudFormation を使用することで、環境の再現性が高まり、開発からテスト、本番環境への展開がスムーズになります。
参考